﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.Drawing;
using System.Drawing.Imaging;

namespace NETXPF.Library
{
	/// <summary>
	/// Implements a thread-safe fast 2D drawing surface, with SetPixel methods for writing
	/// </summary>
	public class FastSurface : IDisposable
	{
		// Constructor
		/// <summary>
		/// 
		/// </summary>
		/// <param name="width"></param>
		/// <param name="height"></param>
		public FastSurface(int width, int height)
		{
			mWidth = width;
			mHeight = height;
			mStride = mWidth;

			int[] buffer = new int[mHeight * mStride];
			mHandle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
			mScan0 = Marshal.UnsafeAddrOfPinnedArrayElement(buffer, 0);
			mBuffer = buffer;
			mBitmap = new Bitmap(mWidth, mHeight, mStride << 2, PixelFormat.Format32bppRgb, mScan0);
		}

		// Fields
		private Bitmap mBitmap;
		private int[] mBuffer;
		private GCHandle mHandle;
		private IntPtr mScan0;

		private int mWidth;
		private int mHeight;
		private int mStride;

		// Methods
		private int PixelFromColor(Color clr)
		{
			return clr.ToArgb();
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="color"></param>
		public void Clear(Color color)
		{
			int c = PixelFromColor(color);
			int count = mWidth * mHeight;
			if (count % 4 == 0)
			{
				for (int i = 0; i < count; i += 4)
				{
					mBuffer[i] = c;
					mBuffer[i + 1] = c;
					mBuffer[i + 2] = c;
					mBuffer[i + 3] = c;
				}
			}
			else
			{
				for (int i = 0; i < count; i++)
					mBuffer[i] = c;
			}
		}
		/// <summary>
		/// 
		/// </summary>
		public void Dispose()
		{
			mBitmap.Dispose();
			mBuffer = null;
			mHandle.Free();
		}
		/// <summary>
		/// 
		/// </summary>
		/// <param name="x"></param>
		/// <param name="y"></param>
		/// <returns></returns>
		public int GetPixel(int x, int y)
		{
			if (x >= mWidth)
				throw new ArgumentOutOfRangeException("x");
			if (y >= mHeight)
				throw new ArgumentOutOfRangeException("y");

			return mBuffer[y * mStride + x];
		}
		/// <summary>
		/// 
		/// </summary>
		/// <param name="x"></param>
		/// <param name="y"></param>
		/// <returns></returns>
		public int GetPixelUnchecked(int x, int y)
		{
			return mBuffer[y * mStride + x];
		}
		/// <summary>
		/// 
		/// </summary>
		/// <param name="x"></param>
		/// <param name="y"></param>
		/// <param name="color"></param>
		public void SetPixel(int x, int y, Color color)
		{
			if (x >= mWidth)
				throw new ArgumentOutOfRangeException("x");
			if (y >= mHeight)
				throw new ArgumentOutOfRangeException("y");

			mBuffer[y * mStride + x] = PixelFromColor(color);
		}
		/// <summary>
		/// 
		/// </summary>
		/// <param name="x"></param>
		/// <param name="y"></param>
		/// <param name="color"></param>
		public void SetPixelUnchecked(int x, int y, Color color)
		{
			mBuffer[y * mStride + x] = PixelFromColor(color);
		}

		/// <summary>
		/// 
		/// </summary>
		/// <returns></returns>
		public Bitmap GetBitmap()
		{
			return mBitmap;
		}
		/// <summary>
		/// 
		/// </summary>
		/// <returns></returns>
		public int[] GetBuffer()
		{
			return mBuffer;
		}
		/// <summary>
		/// 
		/// </summary>
		public int GetWidth()
		{
			return mWidth;
		}
		/// <summary>
		/// 
		/// </summary>
		/// <returns></returns>
		public int GetHeight()
		{
			return mHeight;
		}

		// Properties
		/// <summary>
		/// 
		/// </summary>
		public Bitmap Bitmap
		{
			get { return mBitmap; }
		}
		/// <summary>
		/// 
		/// </summary>
		public int[] Buffer
		{
			get { return mBuffer; }
		}
		/// <summary>
		/// 
		/// </summary>
		public int Width
		{
			get { return mWidth; }
		}
		/// <summary>
		/// 
		/// </summary>
		public int Height
		{
			get { return mHeight; }
		}
	}
}
